package cn.buk.qms.service;

import cn.buk.alert.service.AlertService;
import cn.buk.api.airskyhk.dto.BaseShoppingResponse;
import cn.buk.api.airskyhk.dto.PaxSegment;
import cn.buk.api.airskyhk.service.NdcService;
import cn.buk.api.airskyhk.service.NdcServiceImpl;
import cn.buk.api.azure.BlobService;
import cn.buk.api.eotms.service.FlightApiService;
import cn.buk.api.eotms.service.FlightApiServiceImpl;
import cn.buk.api.pay.alipay.service.AlipayService;
import cn.buk.api.pay.alipay.service.AlipayServiceImpl;
import cn.buk.api.vaas.service.AirportService;
import cn.buk.api.vaas.service.AirportServiceImpl;
import cn.buk.api.wechat.dto.WxTemplateSend;
import cn.buk.api.wechat.entity.WeixinUser;
import cn.buk.api.wechat.service.WeixinService;
import cn.buk.api.wechat.work.dto.UploadMediaResponse;
import cn.buk.api.wechat.work.message.CardButton;
import cn.buk.api.wechat.work.message.TaskCard;
import cn.buk.api.wechat.work.message.TaskCardMessage;
import cn.buk.api.wechat.work.service.WorkWeixinService;
import cn.buk.common.JsonResult;
import cn.buk.common.dto.CommonDto;
import cn.buk.common.dto.QinfoMessage;
import cn.buk.common.dto.eterm.PnrPassengerDto;
import cn.buk.common.flight.dto.FlightInfoDto;
import cn.buk.common.flight.dto.SubClassDto;
import cn.buk.jedis.RedisDao;
import cn.buk.qms.BillUtil;
import cn.buk.tms.dto.*;
import cn.buk.tms.entity.config.EntEotmsConfig;
import cn.buk.tms.entity.config.EnterpriseConfig;
import cn.buk.tms.entity.config.EtermEntConfig;
import cn.buk.tms.entity.order.OrderAttachment;
import cn.buk.tms.sc.SearchBillInfoCriteria;
import cn.buk.tms.bill.service.BillImportService;
import cn.buk.tms.car.dao.CarOrderDao;
import cn.buk.tms.common.constant.TmsConstant;
import cn.buk.tms.dao.*;
import cn.buk.tms.dto.AttachmentDto;
import cn.buk.tms.dto.LoginInfoDto;
import cn.buk.tms.dto.SmsDetailDto;
import cn.buk.tms.entity.base.Airline;
import cn.buk.tms.entity.base.Airport;
import cn.buk.tms.entity.bill.BillInfo;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.entity.order.flight.*;
import cn.buk.tms.entity.order.vas.VasOrder;
import cn.buk.tms.entity.order.vas.VasOrderFlight;
import cn.buk.tms.flight.util.FlightOrderUtil;
import cn.buk.common.util.BukEtermClient;
import cn.buk.common.util.BukEtermClientPoolFactory;
import cn.buk.common.util.CheckUtil;
import cn.buk.common.util.DateUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import jakarta.validation.constraints.NotNull;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.buk.common.Constant.DATE_YYYYMMDD;
import static cn.buk.common.Constant.DATE_YYYY_MM_DD;
import static cn.buk.tms.common.constant.TmsConstant.ID_TYPE_NI;
import static cn.buk.tms.common.constant.TmsConstant.ID_TYPE_PASSPORT;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.poi.ss.usermodel.CellType.NUMERIC;
import static org.apache.poi.ss.usermodel.CellType.STRING;

/**
 * @author yfdai
 */
public abstract class BaseAbstractService {

  private static final Logger logger = LogManager.getLogger(BaseAbstractService.class);

  /**
   * 为不同的租户保存eterm连接池
   */
  private static final HashMap<Integer, GenericObjectPool<BukEtermClient>> etermPoolMap = new HashMap<>();

  @Value("${owner_enterprise_id}")
  private int ownerId;

  @Autowired
  protected RedisDao redisDao;

  private static final String MSG_DATA_FORMAT_IS_INVALID = "不符合格式要求";


  /**
   * 线程池
   */
  protected final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
          1, 10, 10, TimeUnit.SECONDS,
          new ArrayBlockingQueue<>(10),
          new ThreadPoolExecutor.DiscardOldestPolicy());


  @Autowired
  private AlertService alertService;

  @Autowired
  protected EnterpriseDao enterpriseDao;

  @Autowired
  protected EnterpriseService enterpriseService;

  @Autowired
  protected SmsService smsService;

  @Autowired
  protected WorkWeixinService workWeixinService;

  @Autowired
  protected WeixinService weixinService;

  @Autowired
  protected BasicDataService basicDataService;

  @Autowired
  protected CustomerService customerService;

  @Autowired
  protected BillService billService;

  @Autowired
  protected BillImportService importService;

  @Autowired
  private ReportDao reportDao;

  @Autowired
  protected BasicDataDao basicDataDao;

  @Autowired
  protected SmsDao smsDao;

  @Autowired
  protected FlightRefundOrderDao refundOrderDao;

  @Autowired
  protected FlightChangeOrderDao changeOrderDao;

  @Autowired
  protected VasOrderDao vasOrderDao;

  @Autowired
  protected CarOrderDao carOrderDao;

  @Autowired
  protected UserDao userDao;

  @Autowired
  protected PassengerDao passengerDao;

  @Autowired
  protected CustomerDao customerDao;

  @Autowired
  protected OrderNoGenerator orderNoGenerator;


  /**
   * 从连接池获取一个连接
   */
  protected BukEtermClient borrowEtermClient(final int enterpriseId) throws Exception {
    GenericObjectPool<BukEtermClient> pool = etermPoolMap.get(enterpriseId);

    if (pool == null) {
      EtermEntConfig cfg = enterpriseDao.searchEtermConfig(enterpriseId);
      pool = BukEtermClientPoolFactory.createBukEtermClientPool(cfg.getHost(), cfg.getPort(), cfg.getUsername(), cfg.getPassword(), 15000, cfg.getMaxConnection());
      etermPoolMap.put(enterpriseId, pool);
    }

    return pool.borrowObject();
  }

  /**
   * 返回一个连接给连接池
   */
  protected void returnEtermClient(final int enterpriseId, BukEtermClient etermClient) {
    GenericObjectPool<BukEtermClient> pool = etermPoolMap.get(enterpriseId);

    if (pool != null) {
      pool.returnObject(etermClient);
    }
  }

  protected void doSendAlert(int enterpriseId, final String content, String userId, int deptId) {
    QinfoMessage msg = new QinfoMessage();
    msg.setEnterpriseId(enterpriseId);

    msg.setMessage(appendSuffixDepartDesc(content, deptId));

    if (userId != null && userId.trim().length() > 0) {
      msg.setWeixin(userId);
    } else if (deptId > 0) {
      msg.setDeptIds(deptId + "");
    } else {
      msg.setWeixin("@all");
    }

    if (alertService != null) {
      threadPool.execute(() -> alertService.sendQinfoAlert(JSON.toJSONString(msg)));
    }
  }

  private String appendSuffixDepartDesc(String content, int deptId) {
    if (deptId == EntDepartment.DEPT_CUSTOMER_SERVICE) {
      return content + "(发给客服部)";
    } else if (deptId == EntDepartment.DEPT_TICKETING) {
      return content + "(发给出票部)";
    } else if (deptId == EntDepartment.DEPT_CASHIER) {
      return content + "(发给财务部)";
    } else if (deptId == EntDepartment.DEPT_ADMINISTRATOR) {
      return content + "(发给管理部)";
    } else {
      return content;
    }
  }

  private TmsTask createTmsTask(int enterpriseId, String title, String content) {
    TmsTask tmsTask = new TmsTask();
    tmsTask.setEnterpriseId(enterpriseId);
    tmsTask.setTitle(title);
    tmsTask.setContent(content);
    tmsTask.setCreateDate(DateUtil.getCurDate());

    return tmsTask;
  }

  private TaskCardMessage createTaskCardMessage(String title, String content, String toParty, String url, int taskId) {
    TaskCardMessage msg = new TaskCardMessage();
    msg.setToparty(toParty);
    msg.setTaskCard(new TaskCard());
    msg.getTaskCard().setTitle(title);
    msg.getTaskCard().setDescription(content);
    msg.getTaskCard().setUrl(url);
    msg.getTaskCard().setTaskId(taskId + "");

    return msg;
  }

  protected void doSendAlertByTaskCardMsg(final int enterpriseId, final String title, final String content,
                                          final String url, final String toParty) {
    TmsTask tmsTask = createTmsTask(enterpriseId, title, content);
    final int taskId = userDao.createTmsTask(tmsTask);

    TaskCardMessage msg = createTaskCardMessage(title, content, toParty, url, taskId);

    CardButton button = new CardButton();
    button.setKey("yes");
    button.setName("点击确认");
    button.setReplaceName("已确认");
    msg.getTaskCard().getButtonList().add(button);
    doSendAlertWithTaskCard(enterpriseId, msg);
  }

  protected void doSendConfirmAlert(final int enterpriseId, final String title, final String content,
                                    final String url, final String toParty, final int orderType, final int orderId) {
    TmsTask tmsTask = createTmsTask(enterpriseId, title, content);
    tmsTask.setCreateDate(DateUtil.getCurDate());
    tmsTask.setOrderType(orderType);
    tmsTask.setOrderId(orderId);
    tmsTask.setTaskType(TmsTask.TMS_TASK_TYPE_SIMPLE);
    final int taskId = userDao.createTmsTask(tmsTask);

    TaskCardMessage msg = createTaskCardMessage(title, content, toParty, url, taskId);

    CardButton button = new CardButton();
    button.setKey("yes");
    button.setName("同意");
    button.setReplaceName("已同意");
    msg.getTaskCard().getButtonList().add(button);

    button = new CardButton();
    button.setKey("no");
    button.setName("拒绝");
    button.setReplaceName("已拒绝");
    msg.getTaskCard().getButtonList().add(button);

    doSendAlertWithTaskCard(enterpriseId, msg);
  }


  protected void doSendAlertWithTaskCard(int enterpriseId, @NotNull TaskCardMessage taskCardMessage) {
    QinfoMessage msg = new QinfoMessage();
    msg.setVersion(1);
    msg.setEnterpriseId(enterpriseId);
    msg.setMsgType("taskcard");
    msg.setMsgContent(JSON.toJSONString(taskCardMessage));

    if (alertService != null) {
      threadPool.execute(() -> alertService.sendQinfoAlert(JSON.toJSONString(msg)));
    }
  }

  /**
   * 发送微信服务号的模板消息
   */
  protected void doSendWeixinTemplateMessage(final int enterpriseId, WxTemplateSend wxTplRq) {
    QinfoMessage msg = new QinfoMessage();
    msg.setVersion(1);
    msg.setEnterpriseId(enterpriseId);
    msg.setMsgType("templateMessage");
    msg.setMsgContent(JSON.toJSONString(wxTplRq));

    if (alertService != null) {
      threadPool.execute(() -> alertService.sendQinfoAlert(JSON.toJSONString(msg)));
    }
  }

  /**
   * 发送需要审批的提醒消息
   */
  protected void doSendAlertApprove(int ownerId, String mobile, String smsContent, String email, String emailSubject, String emailBody) {
    QinfoMessage msg = new QinfoMessage();
    msg.setEnterpriseId(ownerId);
    msg.setWeixin("NONE"); //不发送给企业微信
    msg.setMobile(mobile);
    msg.setSmsContent(smsContent);
    msg.setEmail(email);
    msg.setEmailSubject(emailSubject);
    msg.setEmailBody(emailBody);
    msg.setMessage("不发送给企业微信"); //目前必须要，发送程序要判断这个字段

    if (alertService != null) {
      threadPool.execute(() -> alertService.sendQinfoAlert(JSON.toJSONString(msg)));
    }
  }

  /**
   * TODO 重构将发信息的功能全部给专门的发信息的代理
   * 发送短信
   *
   * @param smsDetail 消息内容
   * @return json result
   */
  protected JsonResult sendSms(final SmsDetail smsDetail) {
    QinfoMessage msg = new QinfoMessage();
    msg.setEnterpriseId(smsDetail.getEnterpriseId());
    msg.setUsername(smsDetail.getOperator());
    msg.setOrderNo(smsDetail.getOrderNo());

    msg.setWeixin("NONE"); //不发送给企业微信
    msg.setMessage("不发送给企业微信"); //目前必须要，发送程序要判断这个字段

    msg.setMobile(smsDetail.getRecvPhoneNo());
    msg.setSmsContent(smsDetail.getSmsContent());

    if (alertService != null) {
      threadPool.execute(() -> alertService.sendQinfoAlert(JSON.toJSONString(msg)));
    }

    return JsonResult.createJsonResult(1, "后台短信发送，请稍后检查短信状态");
  }

  protected void execSendSms(final String mobile, final String content) {
    SmsDetail smsDetail = SmsDetail.createSmsDetail(this.ownerId, "", mobile, content, "");
    this.sendSms(smsDetail);
  }

  /**
   * @param validationMode 航班起飞时间校验模式
   *                       0 - 宽松
   *                       1 - 严格
   */
  protected JsonResult validateFlightInfo(FlightInfoDto info, final int validationMode) {
    if (!FlightOrderUtil.isValidFlightNo(info.getFlightNo())) {
      return JsonResult.createJsonResult(-5, "航班号(" + info.getFlightNo() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }
    if (!FlightOrderUtil.isValidSubClass(info.getSubclass())) {
      return JsonResult.createJsonResult(-5, "舱位(" + info.getSubclass() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }
    if (!FlightOrderUtil.isValidAirportCode(info.getDport())) {
      return JsonResult.createJsonResult(-5, "出发(" + info.getDport() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }
    if (!FlightOrderUtil.isValidAirportCode(info.getAport())) {
      return JsonResult.createJsonResult(-5, "到达(" + info.getAport() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }

    if (info.getDdate() == null || !CheckUtil.isDate(info.getDdateFormatted())) {
      return JsonResult.createJsonResult(-5, "出发日期(" + info.getDdateFormatted() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    } else if (validationMode == 1 && info.getDdate().getTime() < DateUtil.getCurDate().getTime()) {
      return JsonResult.createJsonResult(-5, "航班起飞日期不能早于当前日期");
    }

    if (validationMode == 1 && !FlightOrderUtil.validateFlightTime(StringUtils.trimToEmpty(info.getDtime()))) {
      return JsonResult
              .createJsonResult(-5, "出发时间(" + info.getDtime() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }
    if (validationMode == 1 && !FlightOrderUtil.validateFlightTime(StringUtils.trimToEmpty(info.getAtime()))) {
      return JsonResult
              .createJsonResult(-5, "到达时间(" + info.getAtime() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }

    if (StringUtils.isNotEmpty(info.getDterm()) && info.getDterm().length() > 2) {
      return JsonResult
              .createJsonResult(-5, "出发航站楼(" + info.getDterm() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }
    if (StringUtils.isNotEmpty(info.getAterm()) && info.getAterm().length() > 2) {
      return JsonResult
              .createJsonResult(-5, "到达航站楼(" + info.getAterm() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }

    if (!FlightOrderUtil.isValidAircraft(info.getAircraft())) {
      return JsonResult.createJsonResult(-5, "机型(" + info.getAircraft() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }

    return null;
  }

  protected JsonResult validatePassengerInfo(PnrPassengerDto psg, final Set<String> mobileSet, final int validationMode) {
    if (StringUtils.isEmpty(psg.getPsgName()) || psg.getPsgName().length() < 2) {
      return JsonResult.createJsonResult(-5, "姓名(" + psg.getPsgName() + ")" + MSG_DATA_FORMAT_IS_INVALID);
    }

    if (psg.getIdType() <= 0) {
      return JsonResult.createJsonResult(-5, "请选择证件类型(" + psg.getName() + ")");
    } else {
      if (psg.getIdNo().length() < 2 && psg.getPsgType() != 2) {
        return JsonResult.createJsonResult(-5, "证件号(" + psg.getIdNo() + ")" + MSG_DATA_FORMAT_IS_INVALID);
      }
      if (psg.getIdType() == ID_TYPE_NI && psg.getIdNo().trim().length() != 18) {
        //身份证的校验应该更准确
        return JsonResult.createJsonResult(-5, "身份证(" + psg.getIdNo() + ")" + MSG_DATA_FORMAT_IS_INVALID);
      } else if (psg.getIdType() == ID_TYPE_PASSPORT && psg.getBirthday() == null) {
//        return JsonResult.createJsonResult(-5, "使用护照时，出生日期必填");
      }
    }

    if (validationMode == 1) {
      //校验手机号
      if (StringUtils.isEmpty(psg.getMobile()) || !CheckUtil.isMobileNo(psg.getMobile())) {
        return JsonResult.createJsonResult(-5, psg.getPsgName() + "手机号(" + psg.getMobile() + ")" + "格式不符");
      }
      mobileSet.add(psg.getMobile());
    }

    return null;
  }

  protected Workbook create(InputStream inp) throws IOException {
    if (!inp.markSupported()) {
      inp = new PushbackInputStream(inp, 8);
    }
    return WorkbookFactory.create(inp);
  }

  protected String getAirlineName(String twoCode) {
    if (basicDataDao == null) {
      return "";
    }

    List<Airline> airlines = basicDataDao.searchAirlines(twoCode);

    return airlines.isEmpty() ? "" : airlines.get(0).getNickName();
  }

  protected int getCabinType(String carrier, String subclass) {
    if (basicDataDao == null) {
      return 0;
    }

    List<SubclassInfo> subclassInfos = basicDataDao.searchSubclasses(carrier, subclass);
    return subclassInfos.isEmpty() ? 0 : subclassInfos.get(0).getCabinType();
  }

  protected String getAirportName(String threeCode) {
    if (basicDataDao == null) {
      return "";
    }

    Airport airport = basicDataDao.getAirport(threeCode);

    return airport == null ? null : getAirportName(airport);
  }

  protected static String getAirportName(Airport airport) {
    String threeCode = airport.getThreeCode();

    String temp = airport.getCityName();

    //哪些城市有多个机场
    if ("SHA".equalsIgnoreCase(threeCode) ||
            "PVG".equalsIgnoreCase(threeCode) ||
            "PEK".equalsIgnoreCase(threeCode) ||
            "PKX".equalsIgnoreCase(threeCode) ||
            "NAY".equalsIgnoreCase(threeCode)) {
      if (airport.getNameCN() != null) {
        temp += airport.getNameCN().trim();
      }
    }

    return temp;
  }


  protected void processOrderAirportName(FlightRefundOrder flightOrder) {
    //检查机场名是否有
    for (FlightRefundOrderDetail flt : flightOrder.getFlights()) {
      processFlightAirportName(flt.getFlight());
    }
  }

  protected void processOrderAirportName(FlightChangeOrder flightOrder) {
    //检查机场名是否有
    for (FlightChangeOrderDetail flt : flightOrder.getFlights()) {
      processFlightAirportName(flt.getFlight());
    }
  }

  protected void processOrderAirportName(FlightOrder flightOrder) {
    //检查机场名是否有
    for (FlightOrderFlight flt : flightOrder.getFlights()) {
      processFlightAirportName(flt.getFlight());
    }
  }

  protected void processOrderAirportName(VasOrder vasOrder) {
    //检查机场名是否有
    for (VasOrderFlight flt : vasOrder.getFlights()) {
      processFlightAirportName(flt.getFlight());
    }
  }

  private void processFlightAirportName(FlightInfo flt) {
    if (flt.getDepartureAirportName() == null) {
      String airportName = getAirportName(flt.getDepartureAirport());
      flt.setDepartureAirportName(airportName);
    }

    if (flt.getArrivalAirportName() == null) {
      String airportName = getAirportName(flt.getArrivalAirport());
      flt.setArrivalAirportName(airportName);
    }

    // 航司名
    String airlineName = getAirlineName(flt.getFlightNo().substring(0, 2));
    flt.setAirlineName(airlineName);

    //舱位类型
    int cabinType = getCabinType(flt.getFlightNo().substring(0, 2), flt.getSubclass());
    flt.setCabinType(cabinType);
  }

  protected String doExecEtermCmd(BukEtermClient eterm, String cmd) throws UnsupportedEncodingException {
    return doExecEtermCmd(0, eterm, cmd);
  }

  protected String doExecEtermCmd(final int enterpriseId, BukEtermClient eterm, String cmd) throws UnsupportedEncodingException {
    logger.info("****** request ****** {}", enterpriseId);
    logger.info(cmd);

    // 执行命令
    String cmdResult = eterm.execCmd(cmd);

    if (cmdResult == null) {
      return null;
    }

    logger.info("****** response ****** {}", enterpriseId);
    cmdResult = cmdResult.substring(7).replace("\r", "\r\n");
    logger.info(cmdResult);

    return cmdResult;
  }


  protected AirportService createAirportService(final int enterpriseId) {
    //TODO 改进参数获取方式
    VaasConfigDto configDto = enterpriseService.searchVaasConfig(enterpriseId);

    if (configDto == null) {
      logger.warn("no vaas config");
      return null;
    }

    if (configDto.getUrl().length() == 0 || configDto.getUsername().length() == 0 || configDto.getSecretKey().length() == 0) {
      logger.warn("vaas config is wrong.");
      return null;
    }

    return new AirportServiceImpl(configDto.getUrl(), configDto.getUsername(), configDto.getSecretKey());
  }

  protected AlipayService createAlipayService(final int enterpriseId) {
    var list = enterpriseDao.searchAlipayConfigs(enterpriseId);
    if (list.isEmpty()) {
      return null;
    }

    var cfg = list.get(0);
    return new AlipayServiceImpl(cfg.getAppId(), cfg.getPrivateKey(), cfg.getAlipayPublicKey(), cfg.getNotifyUrl(), cfg.getReturnUrl());
  }

  /**
   * 判断对当前的用户是否需要隐藏客户名称
   */
  protected boolean isNeedHideCustomerName(final int enterpriseId, final int userId) {
    //增加针对enterpriseId是否启用 隐藏客户名称 的判断 2019-12-14
    if (!enterpriseDao.getHideCustomerName(enterpriseId)) {
      return false;
    }

    Set<String> roles = userDao.loadUserRoles(userId);

    return !(roles.contains(RoleInfo.ROLE_CASHIER) || roles.contains(RoleInfo.ROLE_ADMIN));
  }


  /**
   * 隐藏客户名称
   */
  protected void maskCustomerName(final Customer customer, final String operator, final String username, final boolean needHideCustomerName) {
    if (customer == null || !needHideCustomerName) {
      return;
    }

    if (operator != null && operator.equalsIgnoreCase(username)) {
      return;
    }

    customer.setVipName("***");
  }

  /**
   * 判断对当前用户是否启用“仅看自己的订单”
   */
  protected boolean isOnlySeeOwn(final int enterpriseId, final int userId) {
    //增加针对enterpriseId是否启用“仅看自己的订单” 的判断 2019-12-19
    if (!enterpriseDao.isOnlySeeOwn(enterpriseId)) {
      return false;
    }

    Set<String> roles = userDao.loadUserRoles(userId);

    if (roles.size() == 1 && roles.contains(RoleInfo.ROLE_OPERATOR)) {
      return true;
    } else {
      return !(roles.contains(RoleInfo.ROLE_CASHIER) || roles.contains(RoleInfo.ROLE_ADMIN));
    }
  }

  protected int getOwnerId() {
    return ownerId;
  }

  protected void execSetRedisContent(final String key, final String newJsonStr, final int seconds) {
    execSetRedisContent(key, newJsonStr, seconds, 0);
  }

  /**
   * 更新redis内容
   *
   * @param force 0-不强制， 1-强制更新
   */
  protected void execSetRedisContent(final String key, final String newJsonStr, final int seconds, final int force) {
    final String oldJsonStr = redisDao.getStr(key);
    if (oldJsonStr == null) {
      //不存在
      redisDao.setAndExpire(key, newJsonStr.getBytes(UTF_8), seconds);
    } else {
      //存在，校验是否发生变化
      if (force == 0) {
        if (!oldJsonStr.equalsIgnoreCase(newJsonStr)) {
          redisDao.setAndExpire(key, newJsonStr.getBytes(UTF_8), seconds);
        }
      } else {
        redisDao.setAndExpire(key, newJsonStr.getBytes(UTF_8), seconds);
      }
    }
  }

  /**
   * 为订单指定审批人
   *
   * @param ownerId    owner id
   * @param user       订单提交人
   * @param mainUser   订单提交人所属客户的主账户
   * @param customerId 客户id
   */
  protected String doAssignOrderApprover(final int ownerId, User user, User mainUser, int customerId) {
    if (user.getPassengerId() == null) {
      //未与企业员工资料关联，则分配给该客户的主账户
      return mainUser.getUsername();
    }

    Passenger psgInfo = passengerDao.getById(user.getPassengerId());
    if (psgInfo == null || psgInfo.getDepartment() == null) {
      //未找到该员工资料或未指定部门，则使用主账户
      return mainUser.getUsername();
    }

    //查找部门审批人
    String approver = null;
    boolean moreParentDepartment = true;
    CustomerDepartment deptInfo = psgInfo.getDepartment();
    while (moreParentDepartment) {
      moreParentDepartment = false;

      //获取审批人信息
      List<CustomerTravelApprover> approvers = customerDao.searchCustomerApprovers(ownerId, customerId, deptInfo.getId());
      if (!approvers.isEmpty()) {
        //有审批人，按照规则选择一个
        approver = approvers.get(0).getUser().getUsername();
      } else {
        //是否有上级部门
        if (deptInfo.getParentDepartmentNo() > 0) {
          deptInfo = enterpriseDao.getCustomerDepartmentByDeptId(ownerId, customerId, deptInfo.getParentDepartmentNo());

          moreParentDepartment = deptInfo != null;
        }
      }
    }

    return approver;
  }

  /**
   * 获取企业客户员工的登录信息
   */
  protected LoginInfoDto getCustomerEmployeeLoginInfo(String username, User user) {
    if (user == null && username == null) {
      throw new RuntimeException("参数不能同时为空");
    }

    if (user == null) {
      user = userDao.getUserByUsername(username);
    }

    LoginInfoDto dto = LoginInfoDto.createByUser(user);

    if (dto.getPassengerId() != null) {
      processPsgInfo(user.getParentEnterprise(), dto.getPassengerId(), dto);
    }

    getBindWeixinUserInfo(user.getParentEnterprise(), user.getEnterpriseId(), user.getId(), user.getUsername(), dto.getWxsUsers());

    return dto;
  }

  private void processPsgInfo(final int enterpriseId, final int psgId, final LoginInfoDto dto) {
    Passenger passenger = enterpriseDao.getPassengerById(enterpriseId, psgId);
    if (passenger == null) {
      return;
    }
    if (passenger.getDepartment() != null) {
      dto.setDepartmentId(passenger.getDepartment().getId());
      dto.setDepartmentName(passenger.getDepartment().getName());
    }
    if (passenger.getPosition() != null) {
      dto.setPositionId(passenger.getPosition().getPositionId());
      dto.setPositionName(passenger.getPosition().getName());
    }

    if (dto.getEmail().length() == 0) {
      dto.setEmail(passenger.getEmail());
    }

    if (dto.getMobile().length() == 0) {
      dto.setMobile(passenger.getMobile());
    }

  }

  protected void getBindWeixinUserInfo(final int ownerId, final int customerEnterpriseId, final int userId,
                                       final String username, List<WeixinServiceUserDto> wxsUsers) {
    List<UserBindWeixin> list = userDao.getUserBindWeixin(ownerId, customerEnterpriseId, userId);
    for (var v : list) {
      if (username.equalsIgnoreCase(v.getUsername())) {
        WeixinServiceUserDto wxsUser = new WeixinServiceUserDto();
        wxsUser.setOpenId(v.getWeixinOpenId());

        WeixinUser wxu = weixinService.searchWeixinUser(ownerId, v.getWeixinOpenId());
        if (wxu != null) {
          wxsUser.setAvatar(wxu.getHeadimgurl());
          wxsUser.setNick(wxu.getNickname());

          wxsUsers.add(wxsUser);
        }
      }
    }
  }

  /**
   * 通过姓名获取乘机人的职位级别
   *
   * @param enterpriseId 企业id
   * @param customerId   客户id
   * @param name         员工姓名
   * @param idType       证件类型
   * @param idNo         证件号码
   */
  protected int doGetPassengerPositionLevel(final int enterpriseId, final int customerId, final String name, final int idType, final String idNo) {
    //只处理身份证和护照
    if (idType != ID_TYPE_NI && idType != ID_TYPE_PASSPORT) {
      return 0;
    }

    List<Passenger> passengers = passengerDao.searchByName(enterpriseId, 0, name);
    passengers = passengers.stream().filter(t -> (t.getCustomer() != null && t.getCustomer().getId() == customerId)).collect(Collectors.toList());

    for (Passenger psg : passengers) {
      for (PassengerIdInfo idInfo : psg.getIdInfos()) {
        if (idInfo.getIdType() == idType && idInfo.getIdNo().equalsIgnoreCase(idNo)) {
          if (psg.getPosition() == null) {
            return 0;
          } else {
            return psg.getPosition().getPositionId();
          }
        }
      }
    }


    return 0;
  }

  protected int getEnterpriseIdByUserId(final int userId) {
    User user = userDao.getUserById(userId);

    return user.getEnterpriseId();
  }

  protected String getUsernameByUserId(final int userId) {
    User user = userDao.getUserById(userId);

    return user.getUsername();
  }

  /**
   * 获取企业客户的可用授信余额
   */
  protected double doGetCustomerCreditAmount(int ownerId, int customerEnterpriseId) {
    EnterpriseBalance balance = enterpriseDao.getEnterpriseBalance(ownerId, customerEnterpriseId);
    return (balance.getCashBalance() + balance.getCreditLimit()) / 100.0;
  }


  /**
   * 查找当前用户允许查看的企业客户的ID列表, 如果为空，则表示允许看所有企业客户的订单
   */
  protected List<Integer> searchAllowedCustomerIds(final int enterpriseId, final int userId) {
    User user = userDao.getUserById(userId);
    if (user == null || StringUtils.isEmpty(user.getCustomerServiceGroupCode())) {
      return new ArrayList<>();
    }

    return enterpriseDao.searchCustomerIdsByGroupCode(enterpriseId, user.getCustomerServiceGroupCode());
  }

  /**
   * 判单用户是否客户查看指定用户的订单
   */
  protected boolean isCustomerAllowed(final int enterpriseId, final int userId, final int customerId) {
    if (customerId <= 0) {
      return true;
    }

    //查看当前用户是否允许查看该客户的订单
    List<Integer> allowedCustomerIds = searchAllowedCustomerIds(enterpriseId, userId);
    if (allowedCustomerIds.isEmpty()) {
      return true;
    } else {
      return allowedCustomerIds.contains(customerId);
    }
  }

  /**
   * 处理订单附件信息
   * 以订单附件信息为基础，添加对应BLOB的信息，并生成SAS（共享访问签名），提供URL以供浏览器查看该文件
   *
   * @param enterpriseId   企业id
   * @param attachments    订单附件信息
   * @param attachmentDtos 返回的处理结果
   */
  protected void processAttachments(final int enterpriseId, final List<OrderAttachment> attachments,
                                    final List<AttachmentDto> attachmentDtos) {
    for (OrderAttachment oa : attachments) {
      AttachmentDto attachmentDto = new AttachmentDto();
      attachmentDto.setFileName(oa.getFileName());
      attachmentDto.setDescription(oa.getDescription());
      attachmentDto.setBlobId(oa.getBlobId());

      AzureStorageFileNameMapping m = enterpriseDao.getBlobMappingById(oa.getBlobId());
      if (m != null) {
        attachmentDto.setBlobName(m.getBlobName());
        attachmentDto.setContentType(m.getContentType());
        attachmentDto.setFileSize(m.getFileSize());
      }

      attachmentDtos.add(attachmentDto);
    }
    if (!attachmentDtos.isEmpty()) {
      Optional<BlobService> opt = enterpriseService.createBlobService(enterpriseId);

      if (opt.isPresent()) {
        final String containerName = enterpriseService.getBlobStorageContainerName(enterpriseId);
        for (AttachmentDto attachmentDto : attachmentDtos) {
          String url = opt.get().generateBlobUrlWithSas(containerName, attachmentDto.getBlobName(), "readonly");
          attachmentDto.setUrl(url);
        }
      }
    }
  }

  protected void processSmsDetails(List<SmsDetail> smsDetails, List<SmsDetailDto> smsDetailDtos) {
    for (SmsDetail sms : smsDetails) {
      SmsDetailDto smsDto = new SmsDetailDto();
      BeanUtils.copyProperties(sms, smsDto);

      smsDetailDtos.add(smsDto);
    }
  }

  protected void doAutoExportAuditedBill(final int enterpriseId, final String username, final int taskId) {
    TmsTask tt = userDao.getTmsTaskById(taskId);
    final Date billAuditDate = tt.getCreateDate();

    //1. 导出账单
    final String filename1 = autoExportAuditedBill(enterpriseId, billAuditDate);
    final String displayName1 = DateUtil.formatDate(billAuditDate, DATE_YYYY_MM_DD) + "日已审核账单.csv";
    autoUploadAndSendFileMsg(enterpriseId, username, filename1, displayName1);

    //2. 导出按供应商统计
    final String filename2 = autoExportSupplierStat(enterpriseId, billAuditDate);
    final String displayName2 = DateUtil.formatDate(billAuditDate, DATE_YYYY_MM_DD) + "日供应商统计.csv";
    autoUploadAndSendFileMsg(enterpriseId, username, filename2, displayName2);

    //3. 导出按支付方式统计
    final String filename3 = autoExportPaymentMethodStat(enterpriseId, billAuditDate);
    final String displayName3 = DateUtil.formatDate(billAuditDate, DATE_YYYY_MM_DD) + "日支付方式统计.csv";
    autoUploadAndSendFileMsg(enterpriseId, username, filename3, displayName3);

    enterpriseDao.updateDailyTaskSendFileTime(enterpriseId, username, taskId);
  }

  private void autoUploadAndSendFileMsg(final int enterpriseId, final String username, final String filename, final String displayName) {
    //2. 上传到azure blob
    int retCode = uploadFile(enterpriseId, username, filename);
    if (retCode <= 0) {
      doSendAlert(enterpriseId, "上传" + displayName + "到azure blob失败", "Dai", 0);
      return;
    }

    //3. 上传到企业微信的临时素材
    UploadMediaResponse uploadResponse = workWeixinService.uploadMedia(enterpriseId, "file", filename, displayName);
    if (uploadResponse.getErrcode() != 0) {
      //上传失败
      doSendAlert(enterpriseId, "向企业微信上传" + displayName + "失败：" + uploadResponse.getErrcode() + ", " + uploadResponse.getErrmsg(), "Dai", 0);
      return;
    }

    //4. 发送文件消息到企业微信
    workWeixinService.sendFileMsg(enterpriseId, uploadResponse.getMediaId(), null, EntDepartment.DEPT_CASHIER + "", null);
  }


  /**
   * 自动导出指定日期的已审核账单
   *
   * @return 文件路径
   */
  private String autoExportAuditedBill(final int enterpriseId, final Date auditDate) {
    final String filename = DateUtil.formatDate(auditDate, DATE_YYYYMMDD) + "-audited-bill.csv";
    String fullname = filename;
    SearchBillInfoCriteria sc = new SearchBillInfoCriteria();
    sc.setBillType("-1");
    sc.setReviewed(1);
    sc.setAuditDateBegin(auditDate);
    sc.setAuditDateEnd(auditDate);

    //输出
    try {
      File outputFile = createOutputFile(filename);
      fullname = outputFile.getCanonicalPath();
      logger.info(fullname);

      PrintWriter out = createPrintWriter(outputFile);

      List<Airport> airports = basicDataService.searchAirports();

      int format = 1;
      int customerId = -1;
      exportBillDefaultFormat(sc, format, enterpriseId, customerId, airports, out);

      out.flush();
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return fullname;
  }

  private File createOutputFile(String filename) {
    return new File(System.getProperty("java.io.tmpdir"), filename);
  }

  /**
   * //        什么是BOM:
   * //        BOM（byte-order mark），即字节顺序标记，它是插入到以UTF-8、UTF16或UTF-32编码Unicode文件开头的特殊标记，用来识别Unicode文件的编码类型。对于UTF-8来说，BOM并不是必须的，因为BOM是用来标记多字节编码文件的编码类型和字节顺序（big-endian或little- endian）。而UTF8中，每个字符的编码有多少位是通过第一个字节来表述的，而且没有big-endian和little-endian的区分，见后述。
   * //
   * //        BOMs 文件头:
   * //        00 00 FE FF    = UTF-32, big-endian
   * //        FF FE 00 00    = UTF-32, little-endian
   * //        EF BB BF       = UTF-8,
   * //        FE FF          = UTF-16, big-endian
   * //        FF FE          = UTF-16, little-endian
   *
   * @return PrintWriter
   */
  private PrintWriter createPrintWriter(final File outputFile) throws IOException {
    // 加上UTF-8文件的标识字符
    FileOutputStream os = new FileOutputStream(outputFile);
    os.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});

    return new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
  }

  private String autoExportSupplierStat(final int enterpriseId, final Date auditDate) {
    final String filename = DateUtil.formatDate(auditDate, DATE_YYYYMMDD) + "-stat-supplier.csv";
    String fullname = filename;

    //输出
    try {
      File outputFile = createOutputFile(filename);
      fullname = outputFile.getCanonicalPath();
      logger.info(fullname);

      PrintWriter out = createPrintWriter(outputFile);

      List<ReportStatisticDto> list = reportDao.statAmountBySupplier(enterpriseId, 1, auditDate, auditDate, null);

      StringBuilder sb = createStatExportTitle("供应商");
      out.print(sb);
      out.flush();


      int index = 0;
      for (ReportStatisticDto dto : list) {
        index++;
        String supplierName = dto.getSupplier() == null ? "" : dto.getSupplier().getName();

        sb = ReportStatisticDto.exportContent(index, supplierName, dto);

        out.print(sb);
        out.flush();
      }

      out.flush();
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return fullname;
  }

  private String autoExportPaymentMethodStat(final int enterpriseId, final Date auditDate) {
    final String filename = DateUtil.formatDate(auditDate, DATE_YYYYMMDD) + "-stat-payment-method.csv";
    String fullname = filename;

    //输出
    try {
      File outputFile = createOutputFile(filename);
      fullname = outputFile.getCanonicalPath();
      logger.info(fullname);

      PrintWriter out = createPrintWriter(outputFile);

      List<ReportStatisticDto> list = reportDao.statAmountByPaymentMethod(enterpriseId, 1, auditDate, auditDate, null);

      StringBuilder sb = createStatExportTitle("支付方式");

      out.print(sb);
      out.flush();


      int index = 0;
      for (ReportStatisticDto dto : list) {
        index++;
        String payName = dto.getPayMethod() == null ? "" : dto.getPayMethod().getName();
        sb = ReportStatisticDto.exportContent(index, payName, dto);

        out.print(sb);
        out.flush();
      }

      out.flush();
      out.close();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return fullname;
  }

  private StringBuilder createStatExportTitle(String statTitle) {
    StringBuilder sb = new StringBuilder();
    sb.append("序号").append(",");
    sb.append(statTitle).append(",");
    sb.append(",");
    sb.append("数量").append(",");
    sb.append("总金额").append(",");
    sb.append("总成本").append(",");
    sb.append("未付成本").append(",");
    sb.append("总利润").append(",");
    sb.append("\r");

    return sb;
  }

  private int uploadFile(final int enterpriseId, final String username, final String filename) {
    int retCode;
    Optional<BlobService> opt = enterpriseService.createBlobService(enterpriseId);

    if (opt.isEmpty()) {
      return -1;
    }

    final String containerName = enterpriseService.getBlobStorageContainerName(enterpriseId);
    if (StringUtils.isEmpty(containerName)) {
      return -2;
    }

    File file = new File(filename);

    String blobName = opt.get().uploadLocalFile(containerName, filename);

    //保存上传文件名和BLOB文件名的映射
    retCode = enterpriseService.saveFileNameMapping(enterpriseId, username, file.getName(), blobName, null, file.length());

    return retCode;
  }

  protected void exportBillDefaultFormat(SearchBillInfoCriteria sc, final int format,
                                         final int enterpriseId, final int customerId,
                                         List<Airport> airports,
                                         PrintWriter out) {
    final HashMap<String, List<DataFieldExportInfo>> map = new HashMap<>();

    final String billType = sc.getBillType();
    List<DataFieldExportInfo> titleExportInfos = BillUtil.getExportInfos(billType, format);

    map.put(billType, titleExportInfos);

    StringBuilder sb = BillUtil.exportTitle(titleExportInfos);
    out.print(sb);
    out.flush();

    sc.setPageNo(1);
    sc.setPageSize(500);
    // 根据客户类型排序
    sc.setOrderBy(1);

    boolean b = true;
    while (b) {
      b = false;

      List<BillInfo> infos = billService
              .searchMyBillInfo(enterpriseId, sc, customerId, true);
      logger.info("BillInfo's count: {}.", infos.size());

      for (BillInfo info : infos) {
        List<DataFieldExportInfo> exportInfos = map.get(info.getBillType());
        if (exportInfos == null) {
          exportInfos = BillUtil.getExportInfos(info.getBillType(), format);
          map.put(info.getBillType(), exportInfos);
        }
        sb = BillUtil.exportContent(info, exportInfos, airports, 1);
        out.print(sb);
      }
      out.flush();

      if (sc.getPageNo() < sc.getPage().getPageTotal()) {
        b = true;
        sc.setPageNo(sc.getPageNo() + 1);
      }
    }
  }

  protected String doGetEntCfg(final int enterpriseId, final String itemCode) {
    final String key = String.format(TmsConstant.OWNER_ENT_CONFIG, enterpriseId, itemCode);
    final String val = redisDao.getStr(key);
    if (val != null) {
      return val;
    } else {
      final String itemValue = enterpriseDao.getEnterpriseConfig(enterpriseId, itemCode);
      if (StringUtils.isNotBlank(itemValue)) {
        redisDao.setAndExpire(key, itemValue.getBytes(UTF_8), 5L * 60);
      }
      return itemValue;
    }
  }

  protected int doGetEntCfg(final int enterpriseId, final String itemCode, final int defaultVal) {
    final String val = doGetEntCfg(enterpriseId, itemCode);
    if (StringUtils.isNumeric(val)) {
      return Integer.parseInt(val);
    } else {
      return defaultVal;
    }
  }

  protected float doGetEntCfg(final int enterpriseId, final String itemCode, final float defaultVal) {
    final String val = doGetEntCfg(enterpriseId, itemCode);
    try {
      return Float.parseFloat(val);
    } catch (Exception ex) {
      return defaultVal;
    }
  }

  protected String doGetEntCfg(final int enterpriseId, final String itemCode, final String defaultVal) {
    final String val = doGetEntCfg(enterpriseId, itemCode);
    return StringUtils.isBlank(val) ? defaultVal : val;
  }

  /**
   * 判断当前用户的角色是否只包含主管角色，而不包含财务和管理员角色
   * 如果包含销售角色，则说明是本公司的销售主管
   */
  protected boolean isOnlySupervisor(final int userId) {
    Set<String> roles = userDao.loadUserRoles(userId);
    if (roles.isEmpty()) {
      return false;
    }

    return roles.contains(RoleInfo.ROLE_SUPERVISOR)
            && !roles.contains(RoleInfo.ROLE_SALE_MAN)
            && !roles.contains(RoleInfo.ROLE_ADMIN)
            && !roles.contains(RoleInfo.ROLE_CASHIER);
  }

  /**
   * 为机票订单获取出票服务费
   *
   * @return -1 - 未找到服务费或条件不符
   */
  protected int getTicketingServiceCharge(FlightOrder o) {
    final int enterpriseId = o.getEnterpriseId();

    if (!cn.buk.tms.flight.util.FlightOrderUtil.isAdultFlightOrder(o)) {
      return -1;
    }

    if (o.getCustomer() == null || StringUtils.isBlank(o.getCustomer().getTemplateCode())) {
      return -1;
    }


    final String tplCode = o.getCustomer().getTemplateCode().trim();
    CustomerServiceTemplate tpl = enterpriseDao.searchCustomerServiceTemplate(enterpriseId, tplCode);
    if (tpl == null) {
      return -1;
    }

    CustomerServiceTemplateDto tplDto = CustomerServiceTemplateDto.createBy(tpl);

    int ticketingFee = tplDto.getFlightTicketingFee();
    List<CustomerServiceFlightRule> flightRules = enterpriseDao.searchCustomerServiceFlightRules(enterpriseId, tplDto.getTemplateCode());
    for (CustomerServiceFlightRule fltRule : flightRules) {
      for (FlightOrderFlight flt : o.getFlights()) {
        if (fltRule.getCarrier().equalsIgnoreCase(flt.getCarrierCode())
                && fltRule.getSubclasses().contains(flt.getFlight().getSubclass())) {
          //使用具体航司的具体舱位的服务费规则
          ticketingFee = fltRule.getFlightTicketingFee();
          break;
        }
      }
    }
    return ticketingFee;
  }

  protected String getStringCellValue(Cell cell) {
    if (cell == null) {
      return null;
    }

    if (cell.getCellType() == STRING) {
      return cell.getStringCellValue().trim();
    } else if (cell.getCellType() == NUMERIC) {
      DataFormatter dataFormatter = new DataFormatter();
      dataFormatter.addFormat("###########", null);
      return dataFormatter.formatCellValue(cell);
    } else {
      logger.info("cell type{} is not expected.", cell.getCellType());
      return null;
    }
  }


  protected NdcService createNdcService(final int enterpriseId) {
    final String uid = doGetEntCfg(enterpriseId, EnterpriseConfig.ENTERPRISE_AIRSKYHK_UID);
    if (StringUtils.isBlank(uid)) {
      return null;
    }
    final String sid = doGetEntCfg(enterpriseId, EnterpriseConfig.ENTERPRISE_AIRSKYHK_SID);
    final String privateKey = doGetEntCfg(enterpriseId, EnterpriseConfig.ENTERPRISE_AIRSKYHK_KEY);
    final String url = doGetEntCfg(enterpriseId, EnterpriseConfig.ENTERPRISE_AIRSKYHK_URL);
    final boolean isTest = !"1".equalsIgnoreCase(doGetEntCfg(enterpriseId, EnterpriseConfig.ENTERPRISE_AIRSKYHK_IS_PRODUCT));

    var impl = new NdcServiceImpl(uid, sid, privateKey, url, isTest);
    impl.setDebug(true);

    return impl;
  }

  /**
   * 一个企业可能会有多个私有供应商及共用供应商，使用时需要指定使用哪个供应商
   * 根据实际参数创建EotmsService
   * @param enterpriseId 企业id
   * @param supplierId 供应商id
   */
  protected FlightApiService createEotmsService(final int enterpriseId, final int supplierId) {
    var tmsCfg = getEntEotmsConfig(enterpriseId, supplierId);

    if (tmsCfg == null) {
      return null;
    }

    final String appId = tmsCfg.getAppId();
    final String appSecret = tmsCfg.getAppSecret();
    final String url = tmsCfg.getGateway();
    var impl = new FlightApiServiceImpl(appId, appSecret, url);
    impl.setUsername(tmsCfg.getUsername());
    impl.setPassword(tmsCfg.getPassword());
    impl.setDebugMode(true);

    return impl;
  }

  protected EntEotmsConfig getEntEotmsConfig(final int enterpriseId, final int supplierId) {
    var cfg = doGetEotmsConfig(enterpriseId, supplierId);
    if (cfg == null) {
      cfg = doGetEotmsConfig(0, supplierId);
    }
    return cfg;
  }

  private EntEotmsConfig doGetEotmsConfig(final int enterpriseId, final int supplierId) {
    var cfgs = enterpriseDao.searchEntEotmsConfig(enterpriseId);
    if (cfgs.isEmpty()) {
      return null;
    }

    for (var cfg : cfgs) {
      if (cfg.getSupplierId() == supplierId) {
        return cfg;
      }
    }

    return null;
  }


  protected void convertBaseShoppingResponse(BaseShoppingResponse rs, CommonDto<FlightInfoDto> results) {
    if (results.getDataList() == null) {
      results.setDataList(new ArrayList<>());
    }
    for (var seg : rs.getSegmentList()) {
      FlightInfoDto flt = convertPaxSegment2FlightInfo(seg);
      results.getDataList().add(flt);
    }

    for (var fare : rs.getFareDetailList()) {

      final String segmentKey = fare.getSegmentRefs().get(0).getSegmentKey();
      final String cabinCode = fare.getSegmentRefs().get(0).getCabinCode();

      if (fare.getSegmentRefs().size() > 1) {
        logger.warn("SegmentRef's size: {}({}).", fare.getSegmentRefs().size(), segmentKey);
      }

      //找到符合的航班
      FlightInfoDto dto = matchSegmentKey2FlightInfo(segmentKey, results);
      if (dto == null) {
        continue;
      }

      //找到对应的舱位，将价格匹配
      for (var subclassDto : dto.getSubClassList()) {
        if (cabinCode.equalsIgnoreCase(subclassDto.getSubClass())) {
          for (var p : fare.getFareComponents().get(0).getFarePriceTypes()) {
            if ("ADT".equalsIgnoreCase(p.getPassengerType())) {
              subclassDto.setPrice((int) p.getBaseAmount());
              subclassDto.setOfferItemId(p.getOfferItemId());
            }
          }
        }
      }
    }

    for (var fltInfo : results.getDataList()) {
      fltInfo.calcLowestPrice();

      var list = fltInfo.getSubClassList().stream().sorted(Comparator.comparing(SubClassDto::getPrice).reversed()).collect(Collectors.toList());
      fltInfo.setSubClassList(list);
    }
  }

  private FlightInfoDto convertPaxSegment2FlightInfo(PaxSegment seg) {
    FlightInfoDto flt = new FlightInfoDto();

    flt.setDport(seg.getDeparture().getAirportCode());
    flt.setAport(seg.getArrival().getAirportCode());
    try {
      flt.setDdate(DateUtil.convertToDate(seg.getDeparture().getScheduledDate(), DATE_YYYY_MM_DD));
      flt.setAdate(DateUtil.convertToDate(seg.getArrival().getScheduledDate(), DATE_YYYY_MM_DD));
    } catch (ParseException e) {
      e.printStackTrace();
    }
    flt.setDtime(seg.getDeparture().getScheduledTime());
    flt.setAtime(seg.getArrival().getScheduledTime());
    flt.setDterm(seg.getDeparture().getTerminalName());
    flt.setAterm(seg.getArrival().getTerminalName());

    flt.setFlightNo(seg.getMarketingCarrierCode() + seg.getMarketingFlightNo());
    flt.setOpFlightNo(seg.getOperatingCarrierCode() + seg.getOperatingFlightNo());

    flt.setAircraft(seg.getCarrierAircraftCode());
    if (StringUtils.isNotEmpty(seg.getStopOverAirportCode())) {
      flt.setStopover(1);
      flt.setStopoverAirportCode(seg.getStopOverAirportCode());
    }

    flt.setLegId(Integer.parseInt(seg.getLegId()));
    flt.setSegmentKey(seg.getSegmentKey());
    if (seg.isMeal()) {
      flt.setMeal("Y");
    }

    for (var cabin : seg.getCabinSeats()) {
      SubClassDto dto = new SubClassDto();
      dto.setSubClass(cabin.getCabinTypeCode());
      dto.setSeatCount(cabin.getSeatsLeft());

      flt.getSubClassList().add(dto);
    }

    return flt;
  }

  private FlightInfoDto matchSegmentKey2FlightInfo(String segmentKey, CommonDto<FlightInfoDto> results) {
    for (var fltInfo : results.getDataList()) {
      if (fltInfo.getSegmentKey().equalsIgnoreCase(segmentKey)) {
        return fltInfo;
      }
    }
    return null;
  }


  /**
   * 是否为测试模式的客户
   */
  protected boolean isCustomerTestMode(Customer c) {
    return c != null && c.getTestMode() == 1;
  }

  /**
   * 客户是否处于停用状态
   */
  protected boolean isCustomerStopped(Customer c) {
    return c != null && c.getStatus() == 0;
  }


}
